/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 draw_adventure_tile.cpp

	$Header: /heroes4/draw_adventure_tile.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "draw_adventure_tile.h"

#include "abstract_bitmap.h"
#include "abstract_tile.h"
#include "alpha_blend.h"
#include "convert_16_bit.h"
#include "metatile_texture.h"
#include "pixel_24.h"
#include "screen_point.h"
#include "transition_mask.h"

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{
	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	int g_blue_overflow_mask;
	int g_inverted_blue_overflow_mask;
	int g_green_overflow_mask;
	int g_inverted_green_overflow_mask;
	int g_red_overflow_mask;
	int g_inverted_red_overflow_mask;
	int g_overflow_mask;
	int g_low_bit_mask;
	int g_inverted_low_bit_mask;

	// ------------------------------------------------------------------------------
	// t_local_pixel_mask_viewer class
	// ------------------------------------------------------------------------------
	class t_local_pixel_mask_viewer : public t_pixel_mask_viewer
	{
	public:
		// Constructor
		t_local_pixel_mask_viewer();

		// Member functions
		virtual void on_pixel_masks_changed();

	private:
		void compute_masks();
	};

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	t_local_pixel_mask_viewer::t_local_pixel_mask_viewer()
	{
		compute_masks();
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_local_pixel_mask_viewer::on_pixel_masks_changed()
	{
		compute_masks();
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void t_local_pixel_mask_viewer::compute_masks()
	{
		g_blue_overflow_mask = ( g_blue_16_bit_mask << 1 ) & ~g_blue_16_bit_mask;
		g_inverted_blue_overflow_mask = ~g_blue_overflow_mask;

		g_green_overflow_mask = ( g_green_16_bit_mask << 1 ) & ~g_green_16_bit_mask;
		g_inverted_green_overflow_mask = ~g_green_overflow_mask;

		g_red_overflow_mask = ( g_red_16_bit_mask << 1 ) & ~g_red_16_bit_mask;
		g_inverted_red_overflow_mask = ~g_red_overflow_mask;

		g_overflow_mask = g_blue_overflow_mask | g_green_overflow_mask | g_red_overflow_mask;

		int blue_low_bit_mask = g_blue_16_bit_mask & ~( g_blue_16_bit_mask << 1 );
		int green_low_bit_mask = g_green_16_bit_mask & ~( g_green_16_bit_mask << 1 );
		int red_low_bit_mask = g_red_16_bit_mask & ~( g_red_16_bit_mask << 1 );

		g_low_bit_mask = blue_low_bit_mask | green_low_bit_mask | red_low_bit_mask;
		g_inverted_low_bit_mask = ~g_low_bit_mask;
	}

	// ------------------------------------------------------------------------------
	// The single instance of t_local_pixel_mask_viewer
	// ------------------------------------------------------------------------------
	t_local_pixel_mask_viewer g_pixel_mask_viewer;

	// ------------------------------------------------------------------------------
	// Lighting functions
	// ------------------------------------------------------------------------------
	inline t_uint16 light_half( t_uint16 color )
	{
		return ( color & g_alpha_half_mask ) >> 1;
	}

	inline t_uint16 light_4th( t_uint16 color )
	{
		return ( color & g_alpha_4th_mask ) >> 2;
	}

	inline t_uint16 light_8th( t_uint16 color )
	{
		return ( color & g_alpha_8th_mask ) >> 3;
	}

	inline t_uint16 light_16th( t_uint16 color )
	{
		return ( color & g_alpha_16th_mask ) >> 4;
	}

	inline t_uint16 do_apply_lighting( t_uint16 color, int lighting, bool x_odd, bool y_odd )
	{
		int const k_significant_bits = 4;
		int const k_lighting_shift = k_tile_vertex_light_bits - k_significant_bits;

		lighting += 1 << (k_lighting_shift - 3);
		int lighting_extra = ( lighting >> (k_lighting_shift - 2) ) & 3;
		lighting >>= k_lighting_shift;
		switch ( lighting_extra )
		{
		case 0:
			break;

		case 1:
			lighting += x_odd & y_odd;
			break;

		case 2:
			lighting += ( x_odd + y_odd ) & 1;
			break;

		case 3:
			lighting += x_odd | y_odd;
			break;
		}

		if ( lighting <= 16 )
		{
			switch ( lighting )
			{
			case 0:
				return t_uint16( 0 );

			case 1:
				return color - light_half( color ) - light_4th( color ) - light_8th( color ) - light_16th( color );

			case 2:
				return color - light_half( color ) - light_4th( color ) - light_8th( color );

			case 3:
				return color - light_half( color ) - light_4th( color ) - light_16th( color );

			case 4:
				return color - light_half( color ) - light_4th( color );

			case 5:
				return color - light_half( color ) - light_8th( color ) - light_16th( color );

			case 6:
				return color - light_half( color ) - light_8th( color );

			case 7:
				return color - light_half( color ) - light_16th( color );

			case 8:
				return color - light_half( color );

			case 9:
				return color - light_4th( color ) - light_8th( color ) - light_16th( color );

			case 10:
				return color - light_4th( color ) - light_8th( color );

			case 11:
				return color - light_4th( color ) - light_16th( color );

			case 12:
				return color - light_4th( color );

			case 13:
				return color - light_8th( color ) - light_16th( color );

			case 14:
				return color - light_8th( color );

			case 15:
				return color - light_16th( color );

			case 16:
				return color;
			}
		}
		else
		{
			int add;
			switch ( lighting - 16 )
			{
			case 1:
				add = light_16th( color );
				break;

			case 2:
				add = light_8th( color );
				break;

			case 3:
				add = light_8th( color ) + light_16th( color );
				break;

			case 4:
				add = light_4th( color );
				break;

			case 5:
				add = light_4th( color ) + light_16th( color );
				break;

			case 6:
				add = light_4th( color ) + light_8th( color );
				break;

			case 7:
				add = light_4th( color ) + light_8th( color ) + light_16th( color );
				break;

			case 8:
				add = light_half( color );
				break;

			case 9:
				add = light_half( color ) + light_16th( color );
				break;

			case 10:
				add = light_half( color ) + light_8th( color );
				break;

			case 11:
				add = light_half( color ) + light_8th( color ) + light_16th( color );
				break;

			case 12:
				add = light_half( color ) + light_4th( color );
				break;

			case 13:
				add = light_half( color ) + light_4th( color ) + light_16th( color );
				break;

			case 14:
				add = light_half( color ) + light_4th( color ) + light_8th( color );
				break;

			case 15:
				add = light_half( color ) + light_4th( color ) + light_8th( color ) + light_16th( color );
				break;

			case 16:
				add = color;
				break;
			}

			int low_bits = int( color ) & g_low_bit_mask;
			int result = ( int( color ) & g_inverted_low_bit_mask ) + ( add & g_inverted_low_bit_mask );
			if ( ( result & g_overflow_mask ) == 0 )
				return result | low_bits;

			if ( ( result & g_blue_overflow_mask ) != 0 )
			{
				result &= g_inverted_blue_overflow_mask;
				result |= g_blue_16_bit_mask;
			}

			if ( ( result & g_green_overflow_mask ) != 0 )
			{
				result &= g_inverted_green_overflow_mask;
				result |= g_green_16_bit_mask;
			}

			if ( ( result & g_red_overflow_mask ) != 0 )
			{
				result &= g_inverted_red_overflow_mask;
				result |= g_red_16_bit_mask;
			}

			return result | low_bits;
		}

		return color;
	}

	// ------------------------------------------------------------------------------
	// Alpha blend two alpha values
	// ------------------------------------------------------------------------------

	inline t_uint8 alpha_half( t_uint8 alpha )
	{
		return alpha >> 1;
	}

	inline t_uint8 alpha_4th( t_uint8 alpha )
	{
		return alpha >> 2;
	}

	inline t_uint8 alpha_8th( t_uint8 alpha )
	{
		return alpha >> 3;
	}

	inline t_uint8 alpha_blend_alpha( t_uint8 foreground, t_uint8 background, unsigned alpha )
	{
		switch ( alpha )
		{
		case 0:
			return background;

		case 2:
			return background - alpha_8th( background ) + alpha_8th( foreground );

		case 4:
			return background - alpha_4th( background ) + alpha_4th( foreground );

		case 8:
			return background - alpha_half( background ) + alpha_half( foreground );

		case 11:
			return foreground - alpha_4th( foreground ) + alpha_4th( background );

		case 13:
			return foreground - alpha_8th( foreground ) + alpha_8th( background );

		case 15:
			return foreground;

		case 1:
		case 3:
		case 5:
		case 6:
		case 7:
			{
				unsigned background_alpha = 16 - alpha;
				return ( foreground * alpha + background * background_alpha ) >> 4;
			}

		case 9:
		case 10:
		case 12:
		case 14:
			{
				++alpha;
				unsigned background_alpha = 16 - alpha;
				return ( foreground * alpha + background * background_alpha ) >> 4;
			}

		default:
			assert( false );
		}

		return foreground;
	}

	// ------------------------------------------------------------------------------
	// Draw the specified section of the adventure tile texture to a composite
	// buffer using a 4-bit transition mask
	// ------------------------------------------------------------------------------
	void draw_adventure_tile_4_bit_mask(
		t_adventure_tile_texture const &	src,
		t_uint16 const *					palette,
		t_composite_tile_texture &			dest,
		int									left,
		int									right,
		t_transition_mask const &			mask )
	{
		assert( mask.get_bits() == 4 );

		int column;
		for ( column = left; column < right; ++column )
		{
			int column_start;
			if ( column < k_adventure_tile_width / 2 )
				column_start = ( k_adventure_tile_width / 2 - column - 1 ) / 2;
			else
				column_start = ( column - k_adventure_tile_width / 2 ) / 2;
			int column_size = k_adventure_tile_height - column_start * 2 - 1;

			t_uint8 const * src_ptr = src.get_column_ptr( column );
			t_uint8 const * mask_ptr = mask.get_column( column );
			int bit_shift = 0;

			t_uint16 * dest_ptr = dest.get_column_ptr( column );

			int run_length = *src_ptr++;
			bool transparent_run = true;

			while ( true )
			{
				if ( run_length > column_size )
					run_length = column_size;

				if ( transparent_run )
				{
					dest_ptr += run_length;
					mask_ptr += ( run_length >> 1 );
					if ( ( run_length & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}
				else
				{
					int run_length_left;
					for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
					{
						*dest_ptr = alpha_blend16( *dest_ptr, palette[ *src_ptr ], ( *mask_ptr >> bit_shift ) & 0xF );
						++dest_ptr;
						++src_ptr;
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				column_size -= run_length;
				if ( column_size <= 0 )
					break;

				run_length = *src_ptr++;
				transparent_run = !transparent_run;
			}
		}
	}

	// ------------------------------------------------------------------------------
	// Draw the specified section of one composite buffer to another composite
	// buffer using a 4-bit transition mask
	// ------------------------------------------------------------------------------
	void draw_adventure_tile_4_bit_mask(
		t_composite_tile_texture const &	src,
		t_composite_tile_texture &			dest,
		int									left,
		int									right,
		t_transition_mask const &			mask )
	{
		assert( mask.get_bits() == 4 );

		int column;
		for ( column = left; column < right; ++column )
		{
			int column_start;
			if ( column < k_adventure_tile_width / 2 )
				column_start = ( k_adventure_tile_width / 2 - column - 1 ) / 2;
			else
				column_start = ( column - k_adventure_tile_width / 2 ) / 2;
			int column_size = k_adventure_tile_height - column_start * 2 - 1;

			t_uint16 const * src_ptr = src.get_column_ptr( column );
			t_uint8 const * mask_ptr = mask.get_column( column );

			t_uint16 * dest_ptr = dest.get_column_ptr( column );

			while ( column_size > 1 )
			{
				*dest_ptr = alpha_blend16( *dest_ptr, *src_ptr, *mask_ptr & 0xF );
				++dest_ptr;
				++src_ptr;

				*dest_ptr = alpha_blend16( *dest_ptr, *src_ptr, ( *mask_ptr >> 4 ) & 0xF );
				++dest_ptr;
				++src_ptr;

				++mask_ptr;
				column_size -= 2;
			}

			if ( column_size > 0 )
				*dest_ptr = alpha_blend16( *dest_ptr, *src_ptr, *mask_ptr & 0xF );
		}
	}

	// ------------------------------------------------------------------------------
	// Draw an adventure tile using a 4-bit transition mask
	// ------------------------------------------------------------------------------
	void draw_adventure_tile_4_bit_mask(
		t_adventure_tile_texture const &	src,
		t_uint16 const *					palette,
		t_abstract_bitmap16 &				dest,
		t_screen_point const &				dest_pos,
		t_screen_rect const &				clip_rect,
		t_quad< int > const &				height_quad,
		t_quad< int > const &				lighting_quad,
		t_transition_mask const &			mask )
	{
		assert( mask.get_bits() == 4 );

		int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
		int top_vertex_dest_y = dest_pos.y - height_quad.top;
		int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
		int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

		t_screen_rect dest_rect;

		// Draw the left half of the tile
		dest_rect.left = dest_pos.x;
		dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
		dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
		dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
		if ( intersect( dest_rect, clip_rect ) )
		{
			dest_rect = intersection( dest_rect, clip_rect );

			int src_x_start = dest_rect.left - dest_pos.x;
			int src_x_end = dest_rect.right - dest_pos.x;

			int lighting_start_step = ( lighting_quad.top - lighting_quad.left ) / ( k_adventure_tile_width / 2 );
			int lighting_end_step = ( lighting_quad.bottom - lighting_quad.left ) / ( k_adventure_tile_width / 2 );

			int lighting_start = lighting_quad.left + lighting_start_step * src_x_start;
			int lighting_end = lighting_quad.left + lighting_end_step * src_x_start;

			bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

			int src_x;
			for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
			{
				int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint8 const * src_ptr = src.get_column_ptr( src_x );
					t_uint8 const * mask_ptr = mask.get_column( src_x );

					int bit_shift = 0;
					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int run_length = *src_ptr++;
					bool transparent_run = true;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = dest_delta_y;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						int src_skip = dest_skip * src_delta_y / dest_delta_y;
						error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

						mask_ptr += ( src_skip >> 1 );
						if ( ( src_skip & 1 ) != 0 )
						{
							bit_shift = 4 - bit_shift;
							if ( bit_shift == 0 )
								++mask_ptr;
						}

						while ( true )
						{
							if ( run_length > src_skip )
							{
								if ( !transparent_run )
									src_ptr += src_skip;
								run_length -= src_skip;
								break;
							}

							if ( !transparent_run )
								src_ptr += run_length;
							src_skip -= run_length;

							run_length = *src_ptr++;
							transparent_run = !transparent_run;
						}
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( transparent_run )
						{
							mask_ptr += ( run_length >> 1 );
							if ( ( run_length & 1 ) != 0 )
							{
								bit_shift = 4 - bit_shift;
								if ( bit_shift == 0 )
									++mask_ptr;
							}
							error_term += run_length * dest_delta_y;
							if ( error_term > dest_delta_y )
							{
								int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
								if ( dest_skip > 0 )
								{
									error_term -= dest_skip * src_delta_y;
									dest_ptr = dest.advance_line( dest_ptr, dest_skip );
									lighting += dest_skip * lighting_step;
									y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
									dest_pixels_to_go -= dest_skip;
								}
							}
						}
						else
						{
							int run_length_left;
							for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
							{
								while ( error_term > 0 )
								{
									*dest_ptr = alpha_blend16( *dest_ptr, do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd ), ( *mask_ptr >> bit_shift ) & 0xF );
									if ( --dest_pixels_to_go <= 0 )
										break;
									dest_ptr = dest.advance_line( dest_ptr );
									lighting += lighting_step;
									y_odd = !y_odd;
									error_term -= src_delta_y;
								}

								if ( dest_pixels_to_go <= 0 )
									break;

								++src_ptr;
								bit_shift = 4 - bit_shift;
								if ( bit_shift == 0 )
									++mask_ptr;
								error_term += dest_delta_y;
							}
						}

						if ( dest_pixels_to_go <= 0 )
							break;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}

				lighting_start += lighting_start_step;
				lighting_end += lighting_end_step;
				x_odd = !x_odd;
			}
		}

		// Draw the right half of the tile
		dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
		dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
		dest_rect.right = dest_pos.x + k_adventure_tile_width;
		dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
		if ( intersect( dest_rect, clip_rect ) )
		{
			dest_rect = intersection( dest_rect, clip_rect );

			int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
			int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

			int lighting_start_step = ( lighting_quad.right - lighting_quad.top ) / ( k_adventure_tile_width / 2 );
			int lighting_end_step = ( lighting_quad.right - lighting_quad.bottom ) / ( k_adventure_tile_width / 2 );

			int lighting_start = lighting_quad.top + lighting_start_step * src_x_start;
			int lighting_end = lighting_quad.bottom + lighting_end_step * src_x_start;

			bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

			int src_x;
			for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
			{
				int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = src_x / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint8 const * src_ptr = src.get_column_ptr( src_x + k_adventure_tile_width / 2 );
					t_uint8 const * mask_ptr = mask.get_column( src_x + k_adventure_tile_width / 2 );

					int bit_shift = 0;
					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int run_length = *src_ptr++;
					bool transparent_run = true;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = dest_delta_y;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						int src_skip = dest_skip * src_delta_y / dest_delta_y;
						error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

						mask_ptr += ( src_skip >> 1 );
						if ( ( src_skip & 1 ) != 0 )
						{
							bit_shift = 4 - bit_shift;
							if ( bit_shift == 0 )
								++mask_ptr;
						}

						while ( true )
						{
							if ( run_length > src_skip )
							{
								if ( !transparent_run )
									src_ptr += src_skip;
								run_length -= src_skip;
								break;
							}

							if ( !transparent_run )
								src_ptr += run_length;
							src_skip -= run_length;

							run_length = *src_ptr++;
							transparent_run = !transparent_run;
						}
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( transparent_run )
						{
							mask_ptr += ( run_length >> 1 );
							if ( ( run_length & 1 ) != 0 )
							{
								bit_shift = 4 - bit_shift;
								if ( bit_shift == 0 )
									++mask_ptr;
							}
							error_term += run_length * dest_delta_y;
							if ( error_term > dest_delta_y )
							{
								int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
								if ( dest_skip > 0 )
								{
									error_term -= dest_skip * src_delta_y;
									dest_ptr = dest.advance_line( dest_ptr, dest_skip );
									lighting += dest_skip * lighting_step;
									y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
									dest_pixels_to_go -= dest_skip;
								}
							}
						}
						else
						{
							int run_length_left;
							for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
							{
								while ( error_term > 0 )
								{
									*dest_ptr = alpha_blend16( *dest_ptr, do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd ), ( *mask_ptr >> bit_shift ) & 0xF );
									if ( --dest_pixels_to_go <= 0 )
										break;
									dest_ptr = dest.advance_line( dest_ptr );
									lighting += lighting_step;
									y_odd = !y_odd;
									error_term -= src_delta_y;
								}

								if ( dest_pixels_to_go <= 0 )
									break;

								++src_ptr;
								bit_shift = 4 - bit_shift;
								if ( bit_shift == 0 )
									++mask_ptr;
								error_term += dest_delta_y;
							}
						}

						if ( dest_pixels_to_go <= 0 )
							break;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}

				lighting_start += lighting_start_step;
				lighting_end += lighting_end_step;
				x_odd = !x_odd;
			}
		}
	}

	// ------------------------------------------------------------------------------
	// Shade an adventure tile helper function.  Draw left half and/or right half
	// ------------------------------------------------------------------------------
	void shade_adventure_tile_helper(
		t_uint16				shade_color,
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_pos,
		t_screen_rect const &	clip_rect,
		t_quad< int > const &	height_quad,
		bool					left,
		bool					right )
	{
		assert( is_normalized( clip_rect ) );
		assert( left || right );

		t_uint16 half_shade_color = alpha_8( shade_color );

		int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
		int top_vertex_dest_y = dest_pos.y - height_quad.top;
		int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
		int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

		t_screen_rect dest_rect;

		if ( left )
		{
			// Draw the left half of the tile
			dest_rect.left = dest_pos.x;
			dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
			dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
			dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
			if ( intersect( dest_rect, clip_rect ) )
			{
				dest_rect = intersection( dest_rect, clip_rect );

				int src_x_start = dest_rect.left - dest_pos.x;
				int src_x_end = dest_rect.right - dest_pos.x;

				int src_x;
				for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
				{
					int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
					int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
					int dest_delta_y = dest_y_end - dest_y_start;

					if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
					{
						int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

						t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x;

						int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
						int dest_pixels_to_go = dest_column_bottom - dest_column_top;

						while ( dest_pixels_to_go > 0 )
						{
							*dest_ptr = alpha_8( *dest_ptr ) + half_shade_color;
							dest_ptr = dest.advance_line( dest_ptr );
							--dest_pixels_to_go;
						}
					}
				}
			}
		}

		if ( right )
		{
			// Draw the right half of the tile
			dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
			dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
			dest_rect.right = dest_pos.x + k_adventure_tile_width;
			dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
			if ( intersect( dest_rect, clip_rect ) )
			{
				dest_rect = intersection( dest_rect, clip_rect );

				int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
				int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

				int src_x;
				for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
				{
					int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
					int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
					int dest_delta_y = dest_y_end - dest_y_start;

					if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
					{
						int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

						t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

						int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
						int dest_pixels_to_go = dest_column_bottom - dest_column_top;

						while ( dest_pixels_to_go > 0 )
						{
							*dest_ptr = alpha_8( *dest_ptr ) + half_shade_color;
							dest_ptr = dest.advance_line( dest_ptr );
							--dest_pixels_to_go;
						}
					}
				}
			}
		}
	}

} // Unnamed namespace

// ------------------------------------------------------------------------------
// t_composite_tile_texture members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_composite_tile_texture::t_column_offset_array const & t_composite_tile_texture::get_column_offset_array()
{
	static int column_offset_array[ k_adventure_tile_width ];
	static bool first_time = true;
	if ( first_time )
	{
		int column_size = 1;
		int offset = 0;

		int column;
		for ( column = 0; column < k_adventure_tile_width / 2; column += 2 )
		{
			int i;
			for ( i = 0; i < 2; ++i )
			{
				column_offset_array[ column + i ] = offset;
				offset += column_size;
			}

			column_size += 2;
		}

		column_size = k_adventure_tile_height - 1;
		for ( ; column < k_adventure_tile_width; column += 2 )
		{
			int i;
			for ( i = 0; i < 2; ++i )
			{
				column_offset_array[ column + i ] = offset;
				offset += column_size;
			}

			column_size -= 2;
		}

		first_time = false;
	}

	return column_offset_array;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_composite_tile_texture::t_composite_tile_texture()
	:	m_data( k_adventure_tile_width * k_adventure_tile_height / 2 )
{
}

// ------------------------------------------------------------------------------
// t_composite_transition_mask members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_composite_transition_mask::exclude( t_transition_mask const & other )
{
	assert( other.get_bits() == 1 );

	int column;
	for ( column = 0; column < k_adventure_tile_width; ++column )
		m_data[ column ] &= ~other.get_column_mask( column );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_composite_transition_mask & t_composite_transition_mask::operator|=( t_transition_mask const & other )
{
	assert( other.get_bits() == 1 );

	int column;
	for ( column = 0; column < k_adventure_tile_width; ++column )
		m_data[ column ] |= other.get_column_mask( column );

	return *this;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_composite_transition_mask & t_composite_transition_mask::operator&=( t_transition_mask const & other )
{
	assert( other.get_bits() == 1 );

	int column;
	for ( column = 0; column < k_adventure_tile_width; ++column )
		m_data[ column ] &= other.get_column_mask( column );

	return *this;
}

// ------------------------------------------------------------------------------
// Free functions
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_uint16 apply_lighting( t_uint16 color, int lighting, bool x_odd, bool y_odd )
{
	return do_apply_lighting( color, lighting, x_odd, y_odd );
}

// ------------------------------------------------------------------------------
// Draw the specified section of the adventure tile texture to a composite
// buffer.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_composite_tile_texture &			dest,
	int									left,
	int									right )
{
	assert( left >= 0 && left < k_adventure_tile_width );
	assert( right > 0 && right <= k_adventure_tile_width );
	assert( left < right );

	int column;
	for ( column = left; column < right; ++column )
	{
		int column_start;
		if ( column < k_adventure_tile_width / 2 )
			column_start = ( k_adventure_tile_width / 2 - column - 1 ) / 2;
		else
			column_start = ( column - k_adventure_tile_width / 2 ) / 2;
		int column_size = k_adventure_tile_height - column_start * 2 - 1;

		t_uint8 const * src_ptr = src.get_column_ptr( column );
		t_uint16 * dest_ptr = dest.get_column_ptr( column );

		int run_length = *src_ptr++;
		bool transparent_run = true;

		while ( true )
		{
			if ( run_length > column_size )
				run_length = column_size;

			if ( transparent_run )
				dest_ptr += run_length;
			else
			{
				int run_length_left;
				for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
					*dest_ptr++ = palette[ *src_ptr++ ];
			}

			column_size -= run_length;
			if ( column_size <= 0 )
				break;

			run_length = *src_ptr++;
			transparent_run = !transparent_run;
		}
	}
}

// ------------------------------------------------------------------------------
// Draw the specified section of the adventure tile texture to a composite
// buffer using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_composite_tile_texture &			dest,
	int									left,
	int									right,
	t_transition_mask const &			mask )
{
	assert( left >= 0 && left < k_adventure_tile_width );
	assert( right > 0 && right <= k_adventure_tile_width );
	assert( left < right );

	if ( mask.get_bits() != 1 )
	{
		draw_adventure_tile_4_bit_mask( src, palette, dest, left, right, mask );
		return;
	}

	int column;
	for ( column = left; column < right; ++column )
	{
		t_uint32 column_mask = mask.get_column_mask( column );
		if ( column_mask == 0 )
			continue;

		int column_start;
		if ( column < k_adventure_tile_width / 2 )
			column_start = ( k_adventure_tile_width / 2 - column - 1 ) / 2;
		else
			column_start = ( column - k_adventure_tile_width / 2 ) / 2;
		int column_size = k_adventure_tile_height - column_start * 2 - 1;

		t_uint8 const * src_ptr = src.get_column_ptr( column );
		t_uint16 * dest_ptr = dest.get_column_ptr( column );

		int run_length = *src_ptr++;
		bool transparent_run = true;

		while ( true )
		{
			if ( run_length > column_size )
				run_length = column_size;

			if ( transparent_run )
			{
				dest_ptr += run_length;
				column_mask >>= run_length;
			}
			else
			{
				int run_length_left;
				for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
				{
					if ( ( column_mask & 1 ) != 0 )
						*dest_ptr = palette[ *src_ptr ];
					++dest_ptr;
					++src_ptr;
					column_mask >>= 1;
				}
			}

			if ( column_mask == 0 )
				break;

			column_size -= run_length;
			if ( column_size <= 0 )
				break;

			run_length = *src_ptr++;
			transparent_run = !transparent_run;
		}
	}
}

// ------------------------------------------------------------------------------
// Draw the specified section of one composite buffer to another composite
// buffer using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_composite_tile_texture &			dest,
	int									left,
	int									right,
	t_transition_mask const &			mask )
{
	assert( left >= 0 && left < k_adventure_tile_width );
	assert( right > 0 && right <= k_adventure_tile_width );
	assert( left < right );

	if ( mask.get_bits() != 1 )
	{
		draw_adventure_tile_4_bit_mask( src, dest, left, right, mask );
		return;
	}

	int column;
	for ( column = left; column < right; ++column )
	{
		t_uint32 column_mask = mask.get_column_mask( column );
		if ( column_mask == 0 )
			continue;

		int column_start;
		if ( column < k_adventure_tile_width / 2 )
			column_start = ( k_adventure_tile_width / 2 - column - 1 ) / 2;
		else
			column_start = ( column - k_adventure_tile_width / 2 ) / 2;
		int column_size = k_adventure_tile_height - column_start * 2 - 1;

		t_uint16 const * src_ptr = src.get_column_ptr( column );
		t_uint16 * dest_ptr = dest.get_column_ptr( column );

		while ( column_size > 1 )
		{
			if ( ( column_mask & 1 ) != 0 )
				*dest_ptr = *src_ptr;
			++dest_ptr;
			++src_ptr;

			if ( ( column_mask & 2 ) != 0 )
				*dest_ptr = *src_ptr;
			++dest_ptr;
			++src_ptr;

			column_mask >>= 2;
			if ( column_mask == 0 )
				break;

			column_size -= 2;
		}

		if ( column_size > 0 && column_mask != 0 )
			*dest_ptr = *src_ptr;
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad )
{
	assert( is_normalized( clip_rect ) );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int lighting_start_step = ( lighting_quad.top - lighting_quad.left ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.bottom - lighting_quad.left ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.left + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.left + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * src_ptr = src.get_column_ptr( src_x );

				int error_term;
				int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
				int lighting = lighting_start;

				int run_length = *src_ptr++;
				bool transparent_run = true;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = dest_delta_y;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;
					lighting += dest_skip * lighting_step;

					int src_skip = dest_skip * src_delta_y / dest_delta_y;
					error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

					while ( true )
					{
						if ( run_length > src_skip )
						{
							if ( !transparent_run )
								src_ptr += src_skip;
							run_length -= src_skip;
							break;
						}

						if ( !transparent_run )
							src_ptr += run_length;
						src_skip -= run_length;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}

				bool y_odd = ( ( dest_y - dest_rect.top ) & 1 ) != 0;

				t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					if ( transparent_run )
					{
						error_term += run_length * dest_delta_y;
						if ( error_term > dest_delta_y )
						{
							int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
							if ( dest_skip > 0 )
							{
								error_term -= dest_skip * src_delta_y;
								dest_ptr = dest.advance_line( dest_ptr, dest_skip );
								lighting += dest_skip * lighting_step;
								y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
								dest_pixels_to_go -= dest_skip;
							}
						}
					}
					else
					{
						int run_length_left;
						for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
						{
							while ( error_term > 0 )
							{
								*dest_ptr = do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd );
								if ( --dest_pixels_to_go <= 0 )
									break;
								dest_ptr = dest.advance_line( dest_ptr );
								lighting += lighting_step;
								y_odd = !y_odd;
								error_term -= src_delta_y;
							}

							if ( dest_pixels_to_go <= 0 )
								break;

							++src_ptr;
							error_term += dest_delta_y;
						}
					}

					if ( dest_pixels_to_go <= 0 )
						break;

					run_length = *src_ptr++;
					transparent_run = !transparent_run;
				}
			}

			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int lighting_start_step = ( lighting_quad.right - lighting_quad.top ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.right - lighting_quad.bottom ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.top + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.bottom + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = src_x / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * src_ptr = src.get_column_ptr( src_x + k_adventure_tile_width / 2 );

				int error_term;
				int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
				int lighting = lighting_start;

				int run_length = *src_ptr++;
				bool transparent_run = true;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = dest_delta_y;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;
					lighting += dest_skip * lighting_step;

					int src_skip = dest_skip * src_delta_y / dest_delta_y;
					error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

					while ( true )
					{
						if ( run_length > src_skip )
						{
							if ( !transparent_run )
								src_ptr += src_skip;
							run_length -= src_skip;
							break;
						}

						if ( !transparent_run )
							src_ptr += run_length;
						src_skip -= run_length;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}

				bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

				t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					if ( transparent_run )
					{
						error_term += run_length * dest_delta_y;
						if ( error_term > dest_delta_y )
						{
							int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
							if ( dest_skip > 0 )
							{
								error_term -= dest_skip * src_delta_y;
								dest_ptr = dest.advance_line( dest_ptr, dest_skip );
								lighting += dest_skip * lighting_step;
								y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
								dest_pixels_to_go -= dest_skip;
							}
						}
					}
					else
					{
						int run_length_left;
						for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
						{
							while ( error_term > 0 )
							{
								*dest_ptr = do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd );	//major performance penalty ~3000
								if ( --dest_pixels_to_go <= 0 )													//major performance penalty ~1500
									break;
								dest_ptr = dest.advance_line( dest_ptr );
								lighting += lighting_step;
								y_odd = !y_odd;
								error_term -= src_delta_y;
							}

							if ( dest_pixels_to_go <= 0 )
								break;

							++src_ptr;
							error_term += dest_delta_y;
						}
					}

					if ( dest_pixels_to_go <= 0 )
						break;

					run_length = *src_ptr++;
					transparent_run = !transparent_run;
				}
			}

			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}
}


// ------------------------------------------------------------------------------
// draw an adventure tile using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_transition_mask const &			mask )
{
	assert( is_normalized( clip_rect ) );

	if ( mask.get_bits() != 1 )
	{
		draw_adventure_tile_4_bit_mask( src, palette, dest, dest_pos, clip_rect, height_quad, lighting_quad, mask );
		return;
	}

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int lighting_start_step = ( lighting_quad.top - lighting_quad.left ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.bottom - lighting_quad.left ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.left + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.left + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			t_uint32 column_mask = mask.get_column_mask( src_x );
			if ( column_mask != 0 )
			{
				int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint8 const * src_ptr = src.get_column_ptr( src_x );

					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int run_length = *src_ptr++;
					bool transparent_run = true;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = dest_delta_y;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						int src_skip = dest_skip * src_delta_y / dest_delta_y;
						column_mask >>= src_skip;
						if ( column_mask == 0 )
							goto next_column_left;

						error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

						while ( true )
						{
							if ( run_length > src_skip )
							{
								if ( !transparent_run )
									src_ptr += src_skip;
								run_length -= src_skip;
								break;
							}

							if ( !transparent_run )
								src_ptr += run_length;
							src_skip -= run_length;

							run_length = *src_ptr++;
							transparent_run = !transparent_run;
						}
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( transparent_run )
						{
							column_mask >>= run_length;
							error_term += run_length * dest_delta_y;
							if ( error_term > dest_delta_y )
							{
								int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
								if ( dest_skip > 0 )
								{
									error_term -= dest_skip * src_delta_y;
									dest_ptr = dest.advance_line( dest_ptr, dest_skip );
									lighting += dest_skip * lighting_step;
									y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
									dest_pixels_to_go -= dest_skip;
								}
							}
						}
						else
						{
							int run_length_left;
							for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
							{
								while ( error_term > 0 )
								{
									if ( ( column_mask & 1 ) != 0 )
										*dest_ptr = do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd );
									if ( --dest_pixels_to_go <= 0 )
										break;
									dest_ptr = dest.advance_line( dest_ptr );
									lighting += lighting_step;
									y_odd = !y_odd;
									error_term -= src_delta_y;
								}

								if ( dest_pixels_to_go <= 0 )
									break;

								++src_ptr;
								column_mask >>= 1;
								error_term += dest_delta_y;
							}
						}

						if ( dest_pixels_to_go <= 0 || column_mask == 0 )
							break;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}
			}

next_column_left:
			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int lighting_start_step = ( lighting_quad.right - lighting_quad.top ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.right - lighting_quad.bottom ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.top + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.bottom + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			t_uint32 column_mask = mask.get_column_mask( src_x + k_adventure_tile_width / 2 );
			if ( column_mask != 0 )
			{
				int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = src_x / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint8 const * src_ptr = src.get_column_ptr( src_x + k_adventure_tile_width / 2 );

					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int run_length = *src_ptr++;
					bool transparent_run = true;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = dest_delta_y;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						int src_skip = dest_skip * src_delta_y / dest_delta_y;
						column_mask >>= src_skip;
						if ( column_mask == 0 )
							goto next_column_right;

						error_term = ( src_skip + 1 ) * dest_delta_y - dest_skip * src_delta_y;

						while ( true )
						{
							if ( run_length > src_skip )
							{
								if ( !transparent_run )
									src_ptr += src_skip;
								run_length -= src_skip;
								break;
							}

							if ( !transparent_run )
								src_ptr += run_length;
							src_skip -= run_length;

							run_length = *src_ptr++;
							transparent_run = !transparent_run;
						}
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( transparent_run )
						{
							column_mask >>= run_length;
							error_term += run_length * dest_delta_y;
							if ( error_term > dest_delta_y )
							{
								int dest_skip = ( error_term - dest_delta_y + src_delta_y - 1 ) / src_delta_y;
								if ( dest_skip > 0 )
								{
									error_term -= dest_skip * src_delta_y;
									dest_ptr = dest.advance_line( dest_ptr, dest_skip );
									lighting += dest_skip * lighting_step;
									y_odd = ( ( dest_skip + y_odd ) & 1 ) != 0;
									dest_pixels_to_go -= dest_skip;
								}
							}
						}
						else
						{
							int run_length_left;
							for ( run_length_left = run_length; run_length_left > 0; --run_length_left )
							{
								while ( error_term > 0 )
								{
									if ( ( column_mask & 1 ) != 0 )
										*dest_ptr = do_apply_lighting( palette[ *src_ptr ], lighting, x_odd, y_odd );
									if ( --dest_pixels_to_go <= 0 )
										break;
									dest_ptr = dest.advance_line( dest_ptr );
									lighting += lighting_step;
									y_odd = !y_odd;
									error_term -= src_delta_y;
								}

								if ( dest_pixels_to_go <= 0 )
									break;

								++src_ptr;
								column_mask >>= 1;
								error_term += dest_delta_y;
							}
						}

						if ( dest_pixels_to_go <= 0 || column_mask == 0 )
							break;

						run_length = *src_ptr++;
						transparent_run = !transparent_run;
					}
				}
			}

next_column_right:
			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}
}

// ------------------------------------------------------------------------------
// draw transition mask, from a composite buffer.  Assumes a 4 bit alpha blending mask.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_transition_mask const &			mask )
{
	assert( is_normalized( clip_rect ) );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int lighting_start_step = ( lighting_quad.top - lighting_quad.left ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.bottom - lighting_quad.left ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.left + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.left + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint16 const * src_ptr = src.get_column_ptr( src_x );
				t_uint8 const * mask_ptr = mask.get_column( src_x );

				int bit_shift = 0;
				int error_term;
				int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
				int lighting = lighting_start;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;
					lighting += dest_skip * lighting_step;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					src_ptr += src_skip;
					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

				t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend16( *dest_ptr, do_apply_lighting( *src_ptr, lighting, x_odd, y_odd ), ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					lighting += lighting_step;
					y_odd = !y_odd;
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						++src_ptr;
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}

			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int lighting_start_step = ( lighting_quad.right - lighting_quad.top ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.right - lighting_quad.bottom ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.top + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.bottom + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = src_x / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint16 const * src_ptr = src.get_column_ptr( src_x + k_adventure_tile_width / 2 );
				t_uint8 const * mask_ptr = mask.get_column( src_x + k_adventure_tile_width / 2 );

				int bit_shift = 0;
				int error_term;
				int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
				int lighting = lighting_start;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;
					lighting += dest_skip * lighting_step;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					src_ptr += src_skip;
					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

				t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend16( *dest_ptr, do_apply_lighting( *src_ptr, lighting, x_odd, y_odd ), ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					lighting += lighting_step;
					y_odd = !y_odd;
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						++src_ptr;
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}

			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}
}

// ------------------------------------------------------------------------------
// draw transition mask, from a composite buffer.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_composite_transition_mask const &	mask )
{
	assert( is_normalized( clip_rect ) );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int lighting_start_step = ( lighting_quad.top - lighting_quad.left ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.bottom - lighting_quad.left ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.left + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.left + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			t_uint32 column_mask = mask.get_column_mask( src_x );
			if ( column_mask != 0 )
			{
				int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint16 const * src_ptr = src.get_column_ptr( src_x );

					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = 0;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						error_term = dest_skip * src_delta_y;
						int src_skip = error_term / dest_delta_y;

						column_mask >>= src_skip;
						if ( column_mask == 0 )
							goto next_column_left;

						error_term %= dest_delta_y;

						src_ptr += src_skip;
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( ( column_mask & 1 ) != 0 )
							*dest_ptr = do_apply_lighting( *src_ptr, lighting, x_odd, y_odd );
						if ( --dest_pixels_to_go <= 0 )
							break;
						dest_ptr = dest.advance_line( dest_ptr );
						lighting += lighting_step;
						y_odd = !y_odd;
						error_term += src_delta_y;

						while ( error_term >= dest_delta_y )
						{
							++src_ptr;
							column_mask >>= 1;
							error_term -= dest_delta_y;
						}

						if ( column_mask == 0 )
							break;
					}
				}
			}

next_column_left:
			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int lighting_start_step = ( lighting_quad.right - lighting_quad.top ) / ( k_adventure_tile_width / 2 );
		int lighting_end_step = ( lighting_quad.right - lighting_quad.bottom ) / ( k_adventure_tile_width / 2 );

		int lighting_start = lighting_quad.top + lighting_start_step * src_x_start;
		int lighting_end = lighting_quad.bottom + lighting_end_step * src_x_start;

		bool x_odd = ( ( dest_rect.left - dest_pos.x ) & 1 ) != 0;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			t_uint32 column_mask = mask.get_column_mask( src_x + k_adventure_tile_width / 2 );
			if ( column_mask != 0 )
			{
				int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int src_y_start = src_x / 2;
					int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

					t_uint16 const * src_ptr = src.get_column_ptr( src_x + k_adventure_tile_width / 2 );

					int error_term;
					int lighting_step = ( lighting_end - lighting_start ) / dest_delta_y;
					int lighting = lighting_start;

					int dest_y;
					if ( dest_y_start > dest_rect.top )
					{
						dest_y = dest_y_start;
						error_term = 0;
					}
					else
					{
						dest_y = dest_rect.top;
						int dest_skip = dest_y - dest_y_start;
						lighting += dest_skip * lighting_step;

						error_term = dest_skip * src_delta_y;
						int src_skip = error_term / dest_delta_y;

						column_mask >>= src_skip;
						if ( column_mask == 0 )
							goto next_column_right;

						error_term %= dest_delta_y;

						src_ptr += src_skip;
					}

					bool y_odd = ( ( dest_y - dest_pos.y ) & 1 ) != 0;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_y;

					while ( true )
					{
						if ( ( column_mask & 1 ) != 0 )
							*dest_ptr = do_apply_lighting( *src_ptr, lighting, x_odd, y_odd );
						if ( --dest_pixels_to_go <= 0 )
							break;
						dest_ptr = dest.advance_line( dest_ptr );
						lighting += lighting_step;
						y_odd = !y_odd;
						error_term += src_delta_y;

						while ( error_term >= dest_delta_y )
						{
							++src_ptr;
							column_mask >>= 1;
							error_term -= dest_delta_y;
						}

						if ( column_mask == 0 )
							break;
					}
				}
			}

next_column_right:
			lighting_start += lighting_start_step;
			lighting_end += lighting_end_step;
			x_odd = !x_odd;
		}
	}
}

// ------------------------------------------------------------------------------
// Fill an adventure tile with specified foreground and background alphas and a
// transition mask
// ------------------------------------------------------------------------------
void fill_adventure_tile_alpha_mask(
	t_uint8						foreground_alpha,
	t_uint8						background_alpha,
	t_abstract_bitmap8 &		dest,
	t_screen_point const &		dest_pos,
	t_screen_rect const &		clip_rect,
	t_quad< int > const &		height_quad,
	t_transition_mask const &	mask )
{
	assert( is_normalized( clip_rect ) );
	assert( mask.get_bits() == 4 );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * mask_ptr = mask.get_column( src_x );

				int bit_shift = 0;
				int error_term;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				t_uint8 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend_alpha( foreground_alpha, background_alpha, ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = src_x / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * mask_ptr = mask.get_column( src_x + k_adventure_tile_width / 2 );

				int bit_shift = 0;
				int error_term;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				t_uint8 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend_alpha( foreground_alpha, background_alpha, ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}
		}
	}
}

// ------------------------------------------------------------------------------
// Compose an adventure tile alpha mask with a specified alpha and a
// transition mask
// ------------------------------------------------------------------------------
void compose_adventure_tile_alpha_mask(
	t_uint8						alpha,
	t_abstract_bitmap8 &		dest,
	t_screen_point const &		dest_pos,
	t_screen_rect const &		clip_rect,
	t_quad< int > const &		height_quad,
	t_transition_mask const &	mask )
{
	assert( is_normalized( clip_rect ) );
	assert( mask.get_bits() == 4 );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = ( k_adventure_tile_width / 2 - src_x - 1 ) / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * mask_ptr = mask.get_column( src_x );

				int bit_shift = 0;
				int error_term;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				t_uint8 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend_alpha( alpha, *dest_ptr, ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int src_y_start = src_x / 2;
				int src_delta_y = k_adventure_tile_height - src_y_start * 2 - 1;

				t_uint8 const * mask_ptr = mask.get_column( src_x + k_adventure_tile_width / 2 );

				int bit_shift = 0;
				int error_term;

				int dest_y;
				if ( dest_y_start > dest_rect.top )
				{
					dest_y = dest_y_start;
					error_term = 0;
				}
				else
				{
					dest_y = dest_rect.top;
					int dest_skip = dest_y - dest_y_start;

					error_term = dest_skip * src_delta_y;
					int src_skip = error_term / dest_delta_y;
					error_term %= dest_delta_y;

					mask_ptr += ( src_skip >> 1 );
					if ( ( src_skip & 1 ) != 0 )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
					}
				}

				t_uint8 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_y ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_y;

				while ( true )
				{
					*dest_ptr = alpha_blend_alpha( alpha, *dest_ptr, ( *mask_ptr >> bit_shift ) & 0xF );
					if ( --dest_pixels_to_go <= 0 )
						break;
					dest_ptr = dest.advance_line( dest_ptr );
					error_term += src_delta_y;

					while ( error_term >= dest_delta_y )
					{
						bit_shift = 4 - bit_shift;
						if ( bit_shift == 0 )
							++mask_ptr;
						error_term -= dest_delta_y;
					}
				}
			}
		}
	}
}

// ------------------------------------------------------------------------------
// Shade an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad )
{
	shade_adventure_tile_helper(
		shade_color,
		dest,
		dest_pos,
		clip_rect,
		height_quad,
		true,
		true );
}

// ------------------------------------------------------------------------------
// Shade half an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile_half(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad,
	bool					left )
{
	shade_adventure_tile_helper(
		shade_color,
		dest,
		dest_pos,
		clip_rect,
		height_quad,
		left,
		!left );
}

// ------------------------------------------------------------------------------
// Shade a quarter of an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile_quarter(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad,
	bool					left,
	bool					top )
{
	assert( is_normalized( clip_rect ) );

	t_uint16 half_shade_color = alpha_8( shade_color );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	if ( left )
	{
		// Draw the left half of the tile
		dest_rect.left = dest_pos.x;
		dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
		dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
		dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
		if ( intersect( dest_rect, clip_rect ) )
		{
			dest_rect = intersection( dest_rect, clip_rect );

			int src_x_start = dest_rect.left - dest_pos.x;
			int src_x_end = dest_rect.right - dest_pos.x;

			int src_x;
			for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
			{
				int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_y_mid = dest_y_start + ( dest_y_end - dest_y_start ) / 2;
				if ( top )
					dest_y_end = dest_y_mid;
				else
					dest_y_start = dest_y_mid;
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_column_top;

					while ( dest_pixels_to_go > 0 )
					{
						*dest_ptr = alpha_8( *dest_ptr ) + half_shade_color;
						dest_ptr = dest.advance_line( dest_ptr );
						--dest_pixels_to_go;
					}
				}
			}
		}
	}
	else
	{
		// Draw the right half of the tile
		dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
		dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
		dest_rect.right = dest_pos.x + k_adventure_tile_width;
		dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
		if ( intersect( dest_rect, clip_rect ) )
		{
			dest_rect = intersection( dest_rect, clip_rect );

			int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
			int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

			int src_x;
			for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
			{
				int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
				int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
				int dest_y_mid = dest_y_start + ( dest_y_end - dest_y_start ) / 2;
				if ( top )
					dest_y_end = dest_y_mid;
				else
					dest_y_start = dest_y_mid;
				int dest_delta_y = dest_y_end - dest_y_start;

				if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
				{
					int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

					t_uint16 * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

					int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
					int dest_pixels_to_go = dest_column_bottom - dest_column_top;

					while ( dest_pixels_to_go > 0 )
					{
						*dest_ptr = alpha_8( *dest_ptr ) + half_shade_color;
						dest_ptr = dest.advance_line( dest_ptr );
						--dest_pixels_to_go;
					}
				}
			}
		}
	}
}
